[t:/]$ 지식_

파이썬 스트림 유니코드 디코딩

2021/02/23

대용량 파일을 다운로드 받는 상황이다. 그런데 주는 쪽에서 구닥다리 euc-kr을 쓰고 있다. 나는 이 파일을 받으면서 필터링 가공을 하고 싶다.

예를 들어 10기가를 다운로드 받는데, 필터링 하면 50메가면 된다. 회사에서 주는 서버가 가상서버라서 메모리도 디스크도 쥐콩만한데, 백업까지 남기고 하면 뭐 불안불안한 상황이다.

그렇다면 스트림 처리를 해야 상책이다. 받으면서 처리하고 최후 결과만 저장한다.

중간 다 자르고 요약하면 아마도 이렇게 된다.

curl | iconv | my_filter.py 

그런데 iconv는 스트림 처리를 하지 않나보다.. top 때리면 메모리 CPU 쭉 빨아먹는다. IO보다 CPU가 남아야 정상인데 CPU까지 빨아먹는다는 것은 청크단위 대량 복사에 의한 처리를 한다고 해석이 된다.

또 하나 스트림 처리는 난해한 구석이 있는데, 유니코드의 세계는 한 글자가 3바이트 또는 멀티바이트인 경우가 대부분이고, 다운로드되는 청크의 끝이 얼라인이 맞지 않으면 이를 디코딩 하기 어렵다. 또한 즐겨쓰는 sys.stdin 이터레이터를 쓰자니 개행문자를 인식못하여 파이프가 깨지고 뭔 쑈를 한다.

파이썬에서는 codesc를 쓰거나 유니코드 지원이 우아한 python3를 쓰는 것이 상책인데 스택오버플로에선 딱 맞는 답이 잘 안 보인다.

이렇게 한다.

sys.stdin, stdout은 fd가 아니므로 바로 익숙한 번호를 쓴다.

i = codecs.open(0, 'rb', encoding='euc-kr')
o = codecs.open(1, 'wb', encoding='utf-8')

with i:
    while True:
        c = i.read(4096)
        if not c:
            break
        o.write(c)

decode.py의 모습이다. 청크를 받아서 이 코드에서 다 처리하려면 여전히 유니코드 멀티바이트 얼라인이 해소되지 않으므로 그냥 때려 쓰는 거다.

이제 다음 파이프에서 익숙한 방법으로 처리한다.

for line in sys.stdin:
    헤헤헤

전체 모습은 이렇게 된다.

curl | ./decode.py | ./my_filter.py

메모리도 CPU도 디스크도 안정적이 된다.

sys.stdin.reconfigure 는 해결책이 되지 않는다. 아마도 파이프로 들어오는 바이트들이 코덱 멀티바이트 얼라인이 맞지 않아서 그런 것 같다. 파이프 깨짐 에러가 발생한 확률이 높다.









[t:/] is not "technology - root". dawnsea, rss